require("racket");
require("rosetta");

backend(autocad);

function rectangleLine(p1, p2, p3, p4) {
  return join(spline(list(p1, p2)),
              spline(list(p2, p3)),
              spline(list(p3, p4)),
              spline(list(p4, p1)));
}

function crater(c, n, p1, p2, p3, p4, radius) {
  return loft(list(rectangleLine(p1, p2, p3, p4),
                   move(addC(c, (multC(n, 0.1 * radius))),
                        circleN(radius, n)),
                   move(addC(c, (multC(n, 0.8 * radius))),
                        circleN(0.7 * radius, n)),
                   move(addC(c, (multC(n, -0.1 * radius))),
                        circleN(0.4 * radius, n))));
}

function wavySkin() {
  return makeSimpleSkin(
           function(u, v) {
             return xyz(u,
                        v,
                        0.4*sin(u+v)+0.1*abs(u-1)*sin(v-1)); },
           makeUvDomain('closed', 0, 'closed', 3,
                        'closed', 0, 'closed', 6));
}

function craterTessellation(skin, nU, nV) {
  return map(function(patch) {
              var p1 = patchObject(patch, uFnMin(), vFnMin());
              var p2 = patchObject(patch, uFnMax(), vFnMin());
              var p3 = patchObject(patch, uFnMax(), vFnMax());
              var p4 = patchObject(patch, uFnMin(), vFnMax());
              return crater(patchObject(patch, uFnHalf(), uFnHalf()),
                            patchNormal(patch, uFnHalf(), uFnHalf()),
                            p1, p2, p3, p4,
                            0.4 * min(xyzR(subC(p1, p2)), xyzR(subC(p1, p4))));
             },
             cut(skin, list(nU, nV)));
}

craterTessellation(wavySkin(), 10, 20);